﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PlaceHR.Processor.Processor.Vacation.Steps;

namespace PlaceHR.Processor.Processor
{
    class VacationProcessor : ProcessorBase<VacationProcessorContext, IList<VacationBase>>
    {
        #region Private Methods

        protected override VacationProcessorContext LoadCurrentContext(Employee employee, IEngine currentEngine)
        {
            if (employee == null || currentEngine == null)
            {
                throw new Exception("employee or currentEngine is null!");
            }

            VacationProcessorContext context = CurrentContext;

            if (context == null)
            {
                context = new VacationProcessorContext
                {
                    CurrentEmployee = employee,
                    CurrentEngine = currentEngine,
                    HRDBContext = currentEngine.HRDBContext
                };
            }

            this.LoadGlobalSettings(employee, currentEngine, context);
            this.LoadVacationSettings(employee, currentEngine, context);

            return context;
        }

        protected override IList<VacationBase> LoadCurrentEntity(Employee employee, IEngine currentEngine)
        {
            var vacationBaseList = CurrentContext.CurrentEngine.HRDBContext.VacationBase.Where(
                s => s.EmployeeID == employee.EmployeeID 
                    && s.Year == CurrentContext.CurrentEngine.Year 
                    && s.Month == CurrentContext.CurrentEngine.Month 
                    && !s.IsDeleted).ToList();
            if (this.IsDataCompleted(vacationBaseList))
            {
                if (!CurrentContext.CurrentEngine.ForceRecalculate)
                {
                    return vacationBaseList;
                }
            }
            else
            {
                this.LoadVacationBase(employee, currentEngine, ref vacationBaseList);
            }

            return vacationBaseList;
        }

        protected override void RegisterStepCalculators()
        {
            this.Register(new AnnualVacationStepCalculator());
            this.Register(new SickVacationStepCalculator());
            this.Register(new CSLVacationStepCalculator());
            this.Register(new LSLVacationStepCalculator());
            this.Register(new FinalizeSalaryStepCalculator());
        }

        public override bool IsValidForProcess
        {
            get {
                return this.CurrentContext != null 
                    && this.CurrentEntity != null 
                    && this.CurrentContext.CurrentVacationSettingsList != null;
            }
        }

        private void LoadVacationSettings(Employee employee, IEngine currentEngine, VacationProcessorContext context)
        {
            string officeCode = employee.OfficeDepartment_lnk.FirstOrDefault().OfficeCode;

            context.CurrentVacationSettingsList = currentEngine.HRDBContext.VacationSettings.Where(p => !p.IsDeleted
                        && (string.IsNullOrWhiteSpace(p.OfficeCode) || p.OfficeCode.Equals(officeCode, StringComparison.OrdinalIgnoreCase))
                        && (string.IsNullOrWhiteSpace(p.EmployeeTypeCode) || p.EmployeeTypeCode.Equals(employee.EmployeeTypeCode, StringComparison.OrdinalIgnoreCase))).ToList();
        }

        private bool IsDataCompleted(List<VacationBase> vacationBaseList)
        {
            if (vacationBaseList == null || vacationBaseList.Count < 1)
            {
                return false;
            }

            bool result = vacationBaseList.Any(p => p.VacationTypeCode.Equals(VacationType.AnnualLeave, StringComparison.OrdinalIgnoreCase))
                         && vacationBaseList.Any(p => p.VacationTypeCode.Equals(VacationType.SickLeave, StringComparison.OrdinalIgnoreCase))
                         && vacationBaseList.Any(p => p.VacationTypeCode.Equals(VacationType.ContinuousServiceLeave, StringComparison.OrdinalIgnoreCase))
                         && vacationBaseList.Any(p => p.VacationTypeCode.Equals(VacationType.LongServiceLeave, StringComparison.OrdinalIgnoreCase));

            return result;
        }

        private void LoadVacationBase(Employee employee, IEngine currentEngine, ref List<VacationBase> vacationBaseList)
        {
            if (vacationBaseList == null)
            {
                vacationBaseList = new List<VacationBase>();
            }

            List<string> vacationTypes = new List<string> 
            { 
                  VacationType.AnnualLeave
                , VacationType.SickLeave
                , VacationType.ContinuousServiceLeave
                , VacationType.LongServiceLeave 
            };

            foreach (var vacationType in vacationTypes)
            {
                if (!vacationBaseList.Any(p => p.VacationTypeCode.Equals(vacationType, StringComparison.OrdinalIgnoreCase)))
                {
                    var newVacationBase = new VacationBase
                    {
                        EmployeeID = employee.EmployeeID,
                        VacationTypeCode = vacationType,
                        Year = (short)currentEngine.Year,
                        Month = (short)currentEngine.Month,
                        InsertTime = DateTime.Now,
                        UpdateTime = DateTime.Now,
                        IsDeleted = false
                    };

                    vacationBaseList.Add(newVacationBase);
                    //currentEngine.HRDBContext.VacationBase.AddObject(newVacationBase);
                }
            }
        }


        private void LoadGlobalSettings(Employee employee, IEngine currentEngine, VacationProcessorContext context)
        {
            GlobalSettings defaultGlobalSetting = currentEngine.HRDBContext.GlobalSettings.FirstOrDefault(p => p.OfficeCode == null);

            string officeCode = employee.OfficeDepartment_lnk.FirstOrDefault().OfficeCode;
            GlobalSettings currentGlobalSetting = currentEngine.HRDBContext.GlobalSettings.FirstOrDefault(p => p.OfficeCode != null && p.OfficeCode.Equals(officeCode, StringComparison.OrdinalIgnoreCase));

            if (defaultGlobalSetting == null || currentGlobalSetting == null)
            {
                throw new Exception("Global setting is invalid");
            }

            context.WorkingDaysPerMonth = currentGlobalSetting.WorkingDaysPerMonth.HasValue ? currentGlobalSetting.WorkingDaysPerMonth.Value : defaultGlobalSetting.WorkingDaysPerMonth.Value;
            context.WorkingHoursPerDay = currentGlobalSetting.WorkingHoursPerDay.HasValue ? currentGlobalSetting.WorkingHoursPerDay.Value : defaultGlobalSetting.WorkingHoursPerDay.Value;
            context.PersonalIncomeTaxbase = currentGlobalSetting.PersonalIncomeTaxBase.HasValue ? currentGlobalSetting.PersonalIncomeTaxBase.Value : defaultGlobalSetting.PersonalIncomeTaxBase.Value;
        }

        #endregion
    }
}
